package main.loading;

import java.util.concurrent.Callable;

import com.jme.util.GameTaskQueueManager;

import logic.common.game.Game;
import logic.nodes.lod.blocks.LeafBlock;
import main.GameCycle;
import main.InitGame;
import map.Map;
import factories.GameFactory;
import fileHandling.MapLoader;
import gameStates.IngameState;
import gameStates.ShadowState;
import gameStates.absGamesStates.AbsIngameState;
import gameStates.absGamesStates.gui.AbsSpawnState;
import gameStates.gui.MapLoadingState;
import gameStates.gui.ScoreBoardState;
import gameStates.gui.SpawnMenuState;
import gameStates.passes.AbsPassManagerState;

public class MatchLoadingProcess extends LoadingProcess {
	
	protected Game game;

	protected Map map;
	protected String scenarioName;
	
	protected AbsIngameState ingameState;
	protected AbsSpawnState spawnState;
	
	protected boolean isServer;
	
	protected AbsPassManagerState passManagerState;
	
	protected GameCycle gameCycle;
	protected int maxTeamPlayers;
	
	public MatchLoadingProcess(MapLoadingState loadingState, String scenarioName, boolean isServer, 
			AbsPassManagerState passManagerState, GameCycle gameCycle) {
		super(loadingState);
		this.scenarioName = scenarioName;
		this.isServer = isServer;
		this.passManagerState = passManagerState;
		this.gameCycle = gameCycle;
		
		maxTeamPlayers = gameCycle.getMaxPlayers() / 2;
	}
	
	@Override
	protected void initLoadingSteps() {		
		addLoadingStep(initScenario());
		addLoadingStep(initIngameState());
		addLoadingStep(initBlocks());
		addLoadingStep(initGame());
		addLoadingStep(initNetwork());
		addLoadingStep(initAsteroidFields());
		
		int asteroids = 10;
		for(int i = 1; i <= asteroids; i++) {
			addLoadingStep(loadAsteroidFields(i == asteroids, i * 5 + 10));
		}
		
		addLoadingStep(generateSpawnStations());
		addLoadingStep(initBotsTeamA());
		addLoadingStep(initBotsTeamB());
		addLoadingStep(setupMap());
		addLoadingStep(finishMatchState());
		addLoadingStep(initHUD());
		addLoadingStep(initSpawnMenu());
		addLoadingStep(initCostumizeWindow());
	}
	
	/**
	 * 1% done.
	 * @return
	 */
	private LoadingStep initScenario() {
		return new LoadingStep(1) {
			@Override
			public boolean perform() throws Exception {
				map = MapLoader.createMap(scenarioName);
				if(map == null) return false;
				
				InitGame.get().setCurrentMap(map);
				
				GameTaskQueueManager.getManager().update(new Callable<Void>() {
					@Override
					public Void call() throws Exception {
						((MapLoadingState)loadingState).setMap(map, gameCycle);
						return null;
					}
				}).get();
				return true;
			}
		};
	}

	/**
	 * 2% done.
	 * @return
	 */
	private LoadingStep initIngameState() {
		return new LoadingStep(2) {
			@Override
			public boolean perform() throws Exception {
				ingameState = new IngameState(passManagerState);
				
				return true;
			}
		};
	}
	
	/**
	 * 3% done.
	 * @return
	 */
	private LoadingStep initBlocks() {
		return new LoadingStep(3) {
			@Override
			public boolean perform() throws Exception {
				map.initBlocks(ingameState);
				return true;
			}
		};
	}
	
	/**
	 * 4% done.
	 * @return
	 */
	private LoadingStep initGame() {
		return new LoadingStep(4) {
			@Override
			public boolean perform() throws Exception {
				game = GameFactory.createGame(map, ingameState, gameCycle);
				return true;
			}
		};
	}
	
	/**
	 * 5% done.
	 * @return
	 */
	private LoadingStep initNetwork() {
		return new LoadingStep(5) {
			@Override
			public boolean perform() throws Exception {
				GameFactory.initSpawnStations(game);
				
				return true;
			}
		};
	}
	
	/**
	 * 10% done.
	 * @return
	 */
	private LoadingStep initAsteroidFields() {
		return new LoadingStep(10) {
			@Override
			public boolean perform() {
				map.initAsteroidFields();
				return true;
			}
		};
	}
	
	/**
	 * 60% done.
	 * @return
	 */
	private LoadingStep loadAsteroidFields(final boolean last, int step) {
		return new LoadingStep(step) {
			@Override
			public boolean perform() throws Exception {
				GameTaskQueueManager.getManager().update(new Callable<Void>() {
					@Override
					public Void call() throws Exception {
						map.fillAsteroidFields(5, last);
						return null;
					}
				}).get();
				return true;
			}
		};
	}
	
	/**
	 * 65% done
	 * @return
	 */
	private LoadingStep generateSpawnStations() {
		return new LoadingStep(65) {
			@Override
			public boolean perform() {
				game.getTeamA().updateConqueredFields();
				game.getTeamB().updateConqueredFields();
				return true;
			}
		};
	}
	
	/**
	 * 70% done
	 * @return
	 */
	private LoadingStep initBotsTeamA() {
		return new LoadingStep(70) {
			@Override
			public boolean perform() {
				GameFactory.botIndex = 0;
				
				if(InitGame.get().isServer() && game.getGameCycle().botsEnabled()) 
					GameFactory.setUpPilotBots(game.getTeamA(), game.getTeamB(), maxTeamPlayers);
				return true;
			}
		};
	}
	
	/**
	 * 75% done
	 * @return
	 */
	private LoadingStep initBotsTeamB() {
		return new LoadingStep(75) {
			@Override
			public boolean perform() {
				if(InitGame.get().isServer() && game.getGameCycle().botsEnabled()) 
					GameFactory.setUpPilotBots(game.getTeamB(), game.getTeamA(), maxTeamPlayers);
				return true;
			}
		};
	}
	
	/**
	 * 80% done
	 * @return
	 */
	private LoadingStep setupMap() {
		return new LoadingStep(80) {
			@Override
			public boolean perform() throws Exception {
				GameTaskQueueManager.getManager().update(new Callable<Void>() {
					@Override
					public Void call() throws Exception {
						ingameState.initNewMap(map);
						return null;
					}
				}).get();
				return true;
			}
		};
	}
	
	/**
	 * 85% done
	 * @return
	 */
	private LoadingStep finishMatchState() {
		return new LoadingStep(85) {
			@Override
			public boolean perform() throws Exception {
				GameFactory.finishMatchState(game, ingameState);
				LeafBlock.updateViewDist();
				return true;
			}
		};
	}

	/**
	 * 90% done
	 * @return
	 */
	private LoadingStep initHUD() {
		return new LoadingStep(90) {
			@Override
			public boolean perform() throws Exception {
				GameTaskQueueManager.getManager().update(new Callable<Void>() {
					@Override
					public Void call() throws Exception {
						String ip = gameCycle.getIP();
						
						InitGame.get().getNetworkState().setIngameState(ingameState);
						if(!isServer) InitGame.get().getClientState().initClient(ip);
						else InitGame.get().getServerState().initServer(ip);
						
						ingameState.initIngameWindows();
						return null;
					}
				}).get();
				return true;
			}
		};
	}
	
	/**
	 * 92% done
	 * @return
	 */
	private LoadingStep initSpawnMenu() {
		return new LoadingStep(92) {
			@Override
			public boolean perform() throws Exception {
				GameTaskQueueManager.getManager().update(new Callable<Void>() {
					@Override
					public Void call() throws Exception {
						spawnState = new SpawnMenuState(game, passManagerState);
						ingameState.setSpawnMenuState(spawnState);
						ingameState.initPlayerKeyInput();
						return null;
					}
				}).get();
				return true;
			}
		};
	}
	
	/**
	 * 100% done
	 * @return
	 */
	private LoadingStep initCostumizeWindow() {
		return new LoadingStep(100) {
			@Override
			public boolean perform() {
				
				return true;
			}
		};
	}
	
	@Override
	public void finish() {
		ShadowState shadowState = new ShadowState(ingameState, passManagerState);
		shadowState.updateShadowSettings();
		loadingState.removeGameState();
		
		ScoreBoardState showPlayersState = new ScoreBoardState(game, passManagerState);
		showPlayersState.setActive(false);
		
		InitGame.get().gameStarted();
		
		InitGame.get().changeIngame(true);
		InitGame.get().toggleSpawnMenu();
		
		shadowState.putAt(6);
	}
	
	@Override
	public void cleanup() {
		super.cleanup();
		ingameState = null;
		game = null;
		gameCycle = null;
		map = null;
		passManagerState = null;
		scenarioName = null;
		spawnState = null;
	}
	
	@Override
	public void abort() {
		loadingState.removeGameState();
		InitGame.get().endCurrentGame();
		InitGame.get().loadingAborted(getLoadingState());
	}
	
	@Override
	public boolean canBeAborted() { return true; }
}